// 王智泉

#include "Xls2Json.h"
#include "BasicExcel.hpp"
#include <iostream>
#include <sstream>
#include <string>
#include <codecvt>
#include <set>
#include <locale>
#include <assert.h>


std::wstring s2ws(const std::string& src)
{
    std::locale sys_locale("");
    
    const char* data_from = src.c_str();
    const char* data_from_end = src.c_str() + src.size();
    const char* data_from_next = 0;
    
    wchar_t* data_to = new wchar_t[src.size() + 1];
    wchar_t* data_to_end = data_to + src.size() + 1;
    wchar_t* data_to_next = 0;
    
    wmemset( data_to, 0, src.size() + 1 );
    
    typedef std::codecvt<wchar_t, char, mbstate_t> convert_facet;
    mbstate_t in_state;
    auto result = std::use_facet<convert_facet>(sys_locale).in(
                                                               in_state, data_from, data_from_end, data_from_next,
                                                               data_to, data_to_end, data_to_next );
    if( result == convert_facet::ok )
    {
        std::wstring dst = data_to;
        delete[] data_to;
        return dst;
    }
    else
    {
        printf( "convert error!\n" );
        delete[] data_to;
        return std::wstring(L"");
    }
}

std::string ws2s(const std::wstring& src)
{
    std::locale sys_locale("");
    
    const wchar_t* data_from = src.c_str();
    const wchar_t* data_from_end = src.c_str() + src.size();
    const wchar_t* data_from_next = 0;
    
    int wchar_size = 4;
    char* data_to = new char[(src.size() + 1) * wchar_size];
    char* data_to_end = data_to + (src.size() + 1) * wchar_size;
    char* data_to_next = 0;
    
    memset( data_to, 0, (src.size() + 1) * wchar_size );
    
    typedef std::codecvt<wchar_t, char, mbstate_t> convert_facet;
    mbstate_t out_state;
    auto result = std::use_facet<convert_facet>(sys_locale).out(
                                                                out_state, data_from, data_from_end, data_from_next,
                                                                data_to, data_to_end, data_to_next );
    if( result == convert_facet::ok )
    {
        std::string dst = data_to;
        delete[] data_to;
        return dst;
    }
    else
    {
        printf( "convert error!\n" );
        delete[] data_to;
        return std::string("");
    }
}

std::string s2utf8(const std::string & str)
{
    std::wstring_convert<std::codecvt_utf8<wchar_t> > conv;
    return conv.to_bytes( s2ws(str) );
}

int split(const string& str, vector<string>& ret_, string sep = ",")
{
    if (str.empty())
    {
        return 0;
    }
    
    string tmp;
    string::size_type pos_begin = str.find_first_not_of(sep);
    string::size_type comma_pos = 0;
    
    while (pos_begin != string::npos)
    {
        comma_pos = str.find(sep, pos_begin);
        if (comma_pos != string::npos)
        {
            tmp = str.substr(pos_begin, comma_pos - pos_begin);
            pos_begin = comma_pos + sep.length();
        }
        else
        {
            tmp = str.substr(pos_begin);
            pos_begin = comma_pos;
        }
        
        if (!tmp.empty())
        {
            ret_.push_back(tmp);
            tmp.clear();
        }
    }
    return 0;
}

using namespace YExcel;

Xls2Json::Xls2Json(void)
: m_max_rows(0)
, m_max_cols(0)
{
}


Xls2Json::~Xls2Json(void)
{
}

void Xls2Json::load_from_cfg() {
    
    Json::Value cfg;
    Json::Reader reader;
    
    std::ifstream inFile("cfg.json", std::ios::binary);
    
    if (!inFile.is_open()) {
        
        Json::StyledWriter writer;
        Json::Value fs;
        fs.append("1.xls");
        fs.append("2.xls");
        cfg["files"] = fs;
        cfg["to_dir"] = "";
        cfg["mode"] = "excel2json";
        cfg["skip"] = 1;
        std::string json_file = writer.write(cfg);
        ofstream ofs;
        ofs.open("cfg.json");
        assert(ofs.is_open());
        ofs<<json_file;
        std::cout << "第一次使用，请先配置好'cfg.json'"+reader.getFormatedErrorMessages()+"\n";
        return;
    }
    
    else if(!reader.parse(inFile, cfg)) {
        std::cout << "加载配置文件'cfg.json'出错："+reader.getFormatedErrorMessages()+"\n";
        return;
    }
    
    m_skip = cfg["skip"].asInt();
    m_to_dir = cfg["to_dir"].asString();
    std::string mode = cfg["mode"].asString();
    
    if (m_to_dir.length() != 0 && m_to_dir[m_to_dir.length() - 1] != '/') {
        m_to_dir += "/";
    }
    
    Json::Value arr = cfg["files"];
    
    if (mode == "excel2json") {
        std::cout << "正在导出到json......\n";
        for (int i = 0; i < arr.size(); i++) {
            to_json(arr[i].asString().c_str());
        }
        std::cout << "完成！ 请按任意键退出程序";
    }
    else if (mode == "json2excel") {
        std::cout << "正在导出到xls......\n";
        for (int i = 0; i < arr.size(); i++) {
            to_xls(arr[i].asString().c_str());
        }
        std::cout << "完成！ 请按任意键退出程序";
    }
    
    
    inFile.close();

}

void Xls2Json::to_json(const char* xlsFile)
{
    BasicExcel e;
    
    std::string file = m_to_dir + std::string(xlsFile);
    
    // 加载excel
    if (!e.Load(file.c_str()))
    {
        std::cout << "打开XLS文件:'" + file + "'错误，请确认文件是否存在，或者被其它程序打开";
        return;
    }
    
    std::cout << "正在打开文件:" + std::string(xlsFile) + "\n";
    
    size_t maxSheets = e.GetTotalWorkSheets();
    for (size_t i = 0; i < maxSheets; ++i)
    {
        this->parser_sheet(e.GetWorksheet(i));
    }
}

void Xls2Json::to_xls(const char* jsonFile) {

    std::ifstream inFile(jsonFile, std::ios::binary);
    
    if (inFile.is_open()) {
        Json::Value root;
        Json::Reader reader;
        if(!reader.parse(inFile, root)) {
            std::cout << "加载Json文件：'"+std::string(jsonFile)+"'出错："+reader.getFormatedErrorMessages()+"\n";
            return;
        }
        
        
        std::cout << "正在导出'" + std::string(jsonFile) + "'中的数据...\n";
        
        
        Json::Value::Members member = root.getMemberNames();
        for(Json::Value::Members::iterator iter = member.begin(); iter != member.end(); ++iter) {
            std::string tableName = *iter;
            std::cout << "表：" + tableName + "...";
            Json::Value tableData = root[tableName];
            BasicExcel e;
            BasicExcelWorksheet* sheet = e.AddWorksheet();
            sheet->Rename(tableName.c_str());
            
            std::string tmp;
            std::cout << "正在收集字段名\n";
            std::set<std::string> setFileds;
            for (int i = 0; i < tableData.size(); i++) {
                Json::Value cellData = tableData[i];
                Json::Value::Members cellMember = cellData.getMemberNames();
                 for(Json::Value::Members::iterator iter2 = cellMember.begin(); iter2 != cellMember.end(); ++iter2) {
                     setFileds.insert(*iter2);
                 }
            }
            
            std::map<std::string, int> mapFileds;
            int cellIdx = 0;
            for (std::set<std::string>::iterator sIter = setFileds.begin(); sIter != setFileds.end(); sIter++) {
                mapFileds[*sIter] = cellIdx;
                sheet->Cell(0, cellIdx)->SetString(sIter->c_str());
                cellIdx++;
            }
            
            for (int i = (int)m_skip; i < tableData.size(); i++) {
                Json::Value cellData = tableData[i];
                Json::Value::Members cellMember = cellData.getMemberNames();
                for(Json::Value::Members::iterator iter2 = cellMember.begin(); iter2 != cellMember.end(); ++iter2) {
                    std::string fieldName = *iter2;
                    cellIdx = mapFileds[fieldName];
                    
                    Json::Value data = cellData[fieldName];
                    if (data.isBool()) {
                        sheet->Cell(i + 1, cellIdx)->SetInteger(data.asBool() ? 1 : 0);
                    }
                    else if (data.isInt() || data.isIntegral()) {
                        sheet->Cell(i + 1, cellIdx)->SetInteger(data.asInt());
                    }
                    else if (data.isDouble()) {
                        sheet->Cell(i + 1, cellIdx)->SetDouble(data.asDouble());
                    }
                    else if (data.isString()) {
                        sheet->Cell(i + 1, cellIdx)->SetString(data.asString().c_str());
                    }
                    else if (data.isArray() || data.isObject()) {
                        Json::FastWriter writer;
                        std::string str = writer.write(data);
                        while (str[str.length() - 1] == '\n') {
                            str = str.substr(0, str.length() - 1);
                        }
                        sheet->Cell(i + 1, cellIdx)->SetString(str.c_str());
                    }
                }
            }
            if (!e.SaveAs((tableName + ".xls").c_str())) {
                std::cout << "保存:'" + tableName + ".xls'失败，该文件可能被其化程序打开或文件所在的目录是否存在!\n";
            }
            else {
                std::cout << " OK!\n";
            }
        }
    }
    
    else  {
        std::cout << "打开Json文件：'"+std::string(jsonFile)+"'出错!\n";
        return;
    }
}

// ======================================================================================
void Xls2Json::parser_sheet(YExcel::BasicExcelWorksheet* sheet)
{
    if (NULL == sheet)
        return;
    
    
    // 得到表名
    std::string tableName = "";
    if (sheet->GetUnicodeSheetName())
        tableName = ws2s(sheet->GetUnicodeSheetName());
    else
        tableName = sheet->GetAnsiSheetName();
    
    std::cout << "导出表:" + tableName + " ";
    
    std::vector<string> fileds =get_fileds(sheet);
    
    Json::Value root;
    Json::StyledWriter writer;
    Json::Value data;
    
    this->read_datas(sheet, fileds, &data);
    root[tableName] = data;
    std::string json_file = writer.write(root);
    ofstream ofs;
    ofs.open(tableName + ".json");
    assert(ofs.is_open());
    ofs<<json_file;
    std::cout << " OK\n";
}

// ======================================================================================
std::vector<std::string> Xls2Json::get_fileds(YExcel::BasicExcelWorksheet* sheet, int skipLine)
{
    // 得到行和列的数量
    m_max_rows = sheet->GetTotalRows();
    m_max_cols = sheet->GetTotalCols();
    m_json_tags.clear();
    std::vector<string> fileds;
    for (size_t c = 0; c < m_max_rows; ++c) // 得到字段名
    {
        BasicExcelCell* cell = sheet->Cell(0, c);
        
        if(cell->Type() == BasicExcelCell::UNDEFINED || c >= m_max_cols)
        {
            m_max_cols = c;        // 表格的宽度只到最后一个非空字段
            break;
        }
        char tmpStr[256] = {0};
        std::string filed = "";
        switch (cell->Type())
        {
            case BasicExcelCell::UNDEFINED: ; break;
            case BasicExcelCell::INT:
                sprintf(tmpStr, "%10d", cell->GetInteger());
                filed = tmpStr;
                break;
                
            case BasicExcelCell::DOUBLE:
                sprintf(tmpStr, "%10.6lf", cell->GetDouble());
                filed = tmpStr;
                break;
                
            case BasicExcelCell::STRING:
                filed = cell->GetString();
                break;
                
            case BasicExcelCell::WSTRING:
                filed = ws2s(cell->GetWString());
                break;
        }
        std::size_t loc = filed.find( "json:", 0 );
        if( loc != string::npos ) {
            filed = filed.substr(4, filed.length() - 4);
            m_json_tags[c] = true;;
        }
        std::cout << "...";
        fileds.push_back(filed);
    }
    return fileds;
}

// ======================================================================================
int Xls2Json::read_datas(YExcel::BasicExcelWorksheet* sheet, const FiledNames& fileds, Json::Value* data)
{
    assert(m_max_cols > 0);
    
    // 得到键值
    std::string cellString;
    for (size_t r= 1 + m_skip; r<m_max_rows; ++r)
    {
        Json::Value line;
        
        // 每一行的数据
        for (size_t c = 0; c < m_max_rows; ++c)
        {
            BasicExcelCell* cell = sheet->Cell(r,c);
            cellString.clear();
            switch (cell->Type())
            {
                case BasicExcelCell::UNDEFINED:
                    printf("          ");
                    break;
                    
                case BasicExcelCell::INT:
                    line[fileds[c]] = cell->GetInteger();
                    break;
                    
                case BasicExcelCell::DOUBLE:
                    line[fileds[c]] = cell->GetDouble();
                    break;
                    
                case BasicExcelCell::STRING:
                {
                    std::string str = cell->GetString();
                    if (m_json_tags.find(c) != m_json_tags.end()) {
                        Json::Reader reader;
                        Json::Value js;
                        if (reader.parse(str, js))
                        {
                            line[fileds[c]] = js;
                        }
                    }
                    else {
                        line[fileds[c]] = str;
                    }
                }
                    break;
                    
                case BasicExcelCell::WSTRING:
                {
                    std::string str = ws2s(cell->GetWString());
                    if (m_json_tags.find(c) != m_json_tags.end()) {
                        Json::Reader reader;
                        Json::Value js;
                        if (reader.parse(str, js))
                        {
                            line[fileds[c]] = js;
                        }
                    }
                    else {
                        line[fileds[c]] = str;
                    }
                }
                    break;
            }
        }
        (*data).append(line);
    }  
    return true;
}